package net.lab1024.sa.admin.module.business.aps.service;

import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.admin.module.business.aps.domain.optaplanner.*;
import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.api.solver.SolverFactory;
import org.optaplanner.core.config.solver.SolverConfig;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * APS OptaPlanner服务
 * 使用OptaPlanner进行生产排程优化
 *
 * @Author 1024创新实验室
 * @Date 2025-01-28
 * @Wechat zhuoda1024
 * @Email lab1024@163.com
 * @Copyright <a href="https://1024lab.net">1024创新实验室</a>
 */
@Slf4j
@Service
public class ApsOptaPlannerService {

    private final ExecutorService executorService = Executors.newFixedThreadPool(2);
    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /**
     * 同步求解排程问题
     */
    public ScheduleSolution solveSchedule(ScheduleSolution problem) {
        log.info("开始求解排程问题: {}", problem.getSolutionName());
        
        try {
            // 创建求解器
            Solver<ScheduleSolution> solver = createSolver();
            
            // 求解
            ScheduleSolution solution = solver.solve(problem);
            
            log.info("排程求解完成: {}", solution.getSolutionSummary());
            return solution;
            
        } catch (Exception e) {
            log.error("排程求解失败", e);
            throw new RuntimeException("排程求解失败: " + e.getMessage(), e);
        }
    }

    /**
     * 异步求解排程问题
     */
    public CompletableFuture<ScheduleSolution> solveScheduleAsync(ScheduleSolution problem) {
        log.info("开始异步求解排程问题: {}", problem.getSolutionName());
        
        return CompletableFuture.supplyAsync(() -> {
            return solveSchedule(problem);
        }, executorService);
    }

    /**
     * 创建求解器
     */
    private Solver<ScheduleSolution> createSolver() {
        SolverConfig solverConfig = new SolverConfig()
                .withSolutionClass(ScheduleSolution.class)
                .withEntityClasses(Task.class)
                .withConstraintProviderClass(ScheduleConstraintProvider.class)
                .withTerminationSpentLimit(Duration.ofMinutes(2)); // 2分钟求解时间限制
        
        SolverFactory<ScheduleSolution> solverFactory = SolverFactory.create(solverConfig);
        return solverFactory.buildSolver();
    }

    /**
     * 创建示例排程问题
     */
    public ScheduleSolution createSampleProblem() {
        log.info("创建示例排程问题");
        
        // 创建资源
        List<Resource> resourceList = createSampleResources();
        
        // 创建时间槽
        List<TimeSlot> timeSlotList = createSampleTimeSlots();
        
        // 创建任务
        List<Task> taskList = createSampleTasks();
        
        ScheduleSolution problem = new ScheduleSolution(resourceList, timeSlotList, taskList, "示例排程问题");
        problem.setScheduleStartTime(LocalDateTime.now().format(formatter));
        problem.setScheduleEndTime(LocalDateTime.now().plusDays(7).format(formatter));
        
        log.info("示例问题创建完成: 资源数={}, 时间槽数={}, 任务数={}", 
                resourceList.size(), timeSlotList.size(), taskList.size());
        
        return problem;
    }

    /**
     * 创建示例资源
     */
    private List<Resource> createSampleResources() {
        List<Resource> resources = new ArrayList<>();
        
        LocalDateTime workStart = LocalDateTime.now().withHour(8).withMinute(0);
        LocalDateTime workEnd = LocalDateTime.now().withHour(18).withMinute(0);
        
        resources.add(new Resource(1L, "生产线A", "PRODUCTION", "主要生产线", 2, workStart, workEnd, true, 1.0));
        resources.add(new Resource(2L, "生产线B", "PRODUCTION", "辅助生产线", 1, workStart, workEnd, true, 0.8));
        resources.add(new Resource(3L, "质检设备", "QUALITY", "质量检测设备", 1, workStart, workEnd, true, 1.2));
        resources.add(new Resource(4L, "包装线", "PACKAGING", "产品包装线", 3, workStart, workEnd, true, 1.0));
        
        return resources;
    }

    /**
     * 创建示例时间槽
     */
    private List<TimeSlot> createSampleTimeSlots() {
        List<TimeSlot> timeSlots = new ArrayList<>();
        
        LocalDateTime startTime = LocalDateTime.now().withHour(8).withMinute(0).withSecond(0).withNano(0);
        
        // 创建一周的时间槽，每个时间槽60分钟
        for (int day = 0; day < 7; day++) {
            LocalDateTime dayStart = startTime.plusDays(day);
            
            // 正常工作时间 8:00-18:00
            for (int hour = 8; hour < 18; hour++) {
                LocalDateTime slotStart = dayStart.withHour(hour);
                timeSlots.add(new TimeSlot(
                        (long) (day * 24 + hour),
                        slotStart,
                        60,
                        "NORMAL",
                        1.0,
                        true
                ));
            }
            
            // 加班时间 18:00-22:00
            for (int hour = 18; hour < 22; hour++) {
                LocalDateTime slotStart = dayStart.withHour(hour);
                timeSlots.add(new TimeSlot(
                        (long) (day * 24 + hour),
                        slotStart,
                        60,
                        "OVERTIME",
                        1.5,
                        true
                ));
            }
        }
        
        return timeSlots;
    }

    /**
     * 创建示例任务
     */
    private List<Task> createSampleTasks() {
        List<Task> tasks = new ArrayList<>();
        
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime weekLater = now.plusDays(7);
        
        tasks.add(new Task(1L, "产品A生产", "生产产品A", 120, 8, now, weekLater, "PRODUCTION", null));
        tasks.add(new Task(2L, "产品B生产", "生产产品B", 90, 7, now, weekLater, "PRODUCTION", null));
        tasks.add(new Task(3L, "产品A质检", "质量检测产品A", 30, 6, now, weekLater, "QUALITY", "1"));
        tasks.add(new Task(4L, "产品B质检", "质量检测产品B", 30, 6, now, weekLater, "QUALITY", "2"));
        tasks.add(new Task(5L, "产品A包装", "包装产品A", 60, 5, now, weekLater, "PACKAGING", "3"));
        tasks.add(new Task(6L, "产品B包装", "包装产品B", 60, 5, now, weekLater, "PACKAGING", "4"));
        tasks.add(new Task(7L, "产品C生产", "生产产品C", 150, 9, now, weekLater, "PRODUCTION", null));
        tasks.add(new Task(8L, "产品C质检", "质量检测产品C", 45, 7, now, weekLater, "QUALITY", "7"));
        tasks.add(new Task(9L, "产品C包装", "包装产品C", 75, 6, now, weekLater, "PACKAGING", "8"));
        tasks.add(new Task(10L, "设备维护", "定期设备维护", 180, 10, now, weekLater, "PRODUCTION", null));
        
        return tasks;
    }

    /**
     * 验证排程解决方案
     */
    public boolean validateSolution(ScheduleSolution solution) {
        if (solution == null || solution.getTaskList() == null) {
            return false;
        }
        
        // 检查是否有未分配的高优先级任务
        long unassignedHighPriorityTasks = solution.getTaskList().stream()
                .filter(task -> task.getPriority() != null && task.getPriority() >= 8)
                .filter(task -> task.getAssignedResource() == null || task.getStartTimeSlot() == null)
                .count();
        
        if (unassignedHighPriorityTasks > 0) {
            log.warn("存在{}个未分配的高优先级任务", unassignedHighPriorityTasks);
        }
        
        return solution.isFeasible();
    }

    /**
     * 获取排程统计信息
     */
    public String getScheduleStatistics(ScheduleSolution solution) {
        if (solution == null) {
            return "无效的解决方案";
        }
        
        StringBuilder stats = new StringBuilder();
        stats.append("=== 排程统计信息 ===\n");
        stats.append(solution.getSolutionSummary()).append("\n");
        
        if (solution.getTaskList() != null) {
            stats.append("\n=== 任务分配详情 ===\n");
            solution.getTaskList().forEach(task -> {
                if (task.getAssignedResource() != null && task.getStartTimeSlot() != null) {
                    stats.append(String.format("任务: %s | 资源: %s | 开始时间: %s | 结束时间: %s\n",
                            task.getTaskName(),
                            task.getAssignedResource().getResourceName(),
                            task.getStartTimeSlot().getStartTime().format(formatter),
                            task.getEndTime().format(formatter)));
                } else {
                    stats.append(String.format("任务: %s | 状态: 未分配\n", task.getTaskName()));
                }
            });
        }
        
        return stats.toString();
    }
}